package com.yhn.bubblevideo.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yhn.bubblevideo.common.enums.BaseErrorCode;
import com.yhn.bubblevideo.common.exception.BusinessException;
import com.yhn.bubblevideo.model.domain.User;
import com.yhn.bubblevideo.model.dto.req.user.UserLoginRequestDTO;
import com.yhn.bubblevideo.model.dto.req.user.UserRegisterRequestDTO;
import com.yhn.bubblevideo.model.dto.resp.UserLoginRespDTO;
import com.yhn.bubblevideo.model.dto.resp.UserRespDTO;
import com.yhn.bubblevideo.user.mapper.UserMapper;
import com.yhn.bubblevideo.user.service.UserService;
import jakarta.annotation.Resource;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import static com.yhn.bubblevideo.common.constant.UserConstant.*;

/**
 * @author yhn
 * @version 1.0
 * @description
 * @date 2024/9/21 10:00
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {


    @Value("${spring.mail.username}")
    private String sendUserName;
    @Resource
    private JavaMailSender javaMailSender;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    @Transactional
    public void userRegister(UserRegisterRequestDTO requestParam) {
        if (requestParam == null) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR);
        }

        // 密码和校验密码相同
        if (!requestParam.getPassword().equals(requestParam.getCheckPassword())) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        // 用户昵称邮箱不能重复
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getNickname, requestParam.getNickname())
                .or()
                .eq(User::getEmail, requestParam.getEmail());

        User existingUser = baseMapper.selectOne(queryWrapper);
        if (existingUser != null) {
            if (existingUser.getNickname().equals(requestParam.getNickname())) {
                throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "用户昵称已存在");
            }
            if (existingUser.getEmail().equals(requestParam.getEmail())) {
                throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "邮箱已存在");
            }
        }

        //校验验证码
        String code = requestParam.getCode();
        String emailCode = stringRedisTemplate.opsForValue().get("email:code:" + requestParam.getEmail());
        if (!code.equals(emailCode)) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "验证码错误");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((USER_SALT + requestParam.getPassword()).getBytes());
        // 3. 插入数据
        User user = new User();
        BeanUtils.copyProperties(requestParam, user);
        user.setPassword(encryptPassword);
        user.setUserAvatar(DEFAULT_USER_AVATAR);
        this.save(user);
        //删除验证码
        stringRedisTemplate.delete("email:code:" + requestParam.getEmail());
    }

    @Override
    public UserLoginRespDTO userLogin(UserLoginRequestDTO requestParam) {
        if (requestParam == null) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR);
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((USER_SALT + requestParam.getPassword()).getBytes());
        // 查询邮箱是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, requestParam.getEmail());
        User user = baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (user == null) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "邮箱不存在");
        }
        if (!user.getPassword().equals(encryptPassword)) {
            throw new BusinessException(BaseErrorCode.PARAMS_ERROR, "密码错误");
        }
        UserLoginRespDTO userLoginResp = getLoginUserVO(user);
        Long userId = userLoginResp.getId();
        if (userId != null) {
            /*
             * 实现同端互斥效果，把该用户Web端的令牌销毁。
             * 在其他浏览器上已经登陆的该账户，令牌就失效了，实现同端互斥。
             */
            StpUtil.logout(userId);
            //通过会话对象，向SaToken传递userId
            StpUtil.login(userId);

//          userLoginResp.setPermission(StpUtil.getPermissionList());
            userLoginResp.setToken(String.valueOf(StpUtil.getTokenInfo().tokenValue));
            // 将 VO 对象存储到会话中
            StpUtil.getSession().set("userLoginResp", userLoginResp);
        }
        return userLoginResp;
    }

    @Override
    public UserLoginRespDTO getLoginUser() {
        return (UserLoginRespDTO) StpUtil.getSession().get("userLoginResp");
    }

    @Override
    public boolean userLogout() {
        // 从令牌中解密出来userId
        Long userId = StpUtil.getLoginIdAsLong();
        // 销毁令牌
        StpUtil.logout(userId);
        return true;
    }

    @Override
    public UserLoginRespDTO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserLoginRespDTO userLoginResp = new UserLoginRespDTO();
        BeanUtils.copyProperties(user, userLoginResp);
        return userLoginResp;
    }

    /**
     * 验证码发送
     *
     * @param email 邮箱
     */
    @Override
    public void sendMessage(String email) {
        if (StringUtils.isBlank(email) || !email.matches(EMAIL_REGEX)) {
            throw new BusinessException("邮箱格式有误");
        }
        //生成六位数的验证码
        String code = String.format("%06d", (int) (Math.random() * 1000000));
        sendEmailCode(code, email);
        //将验证码存入redis中
        stringRedisTemplate.opsForValue().set("email:code:" + email, code, 5, TimeUnit.MINUTES);
    }

    /**
     * 将验证码发送到手机
     *
     * @param code 验证码
     */
    private void sendEmailCode(String code, String email) {
        try {
            System.getProperties().setProperty("mail.mime.address.usecanonicalhostname", "false");
            MimeMessage message = javaMailSender.createMimeMessage();
            Session session = message.getSession();
            session.setDebug(true);
            //  解决本地DNS未配置 ip->域名场景下，邮件发送太慢的问题
            session.getProperties().setProperty("mail.smtp.localhost", "myComputer");
            MimeMessageHelper helper = new MimeMessageHelper(message);
            helper.setFrom(sendUserName);
            //设置收件人
            helper.setTo(email);
            helper.setSubject("注册邮箱");
            helper.setText("【泡泡视界】你的验证码是：" + code + "，5分钟内有效！");
            helper.setSentDate(new Date());
            javaMailSender.send(message);
        } catch (Exception e) {
            throw new BusinessException("邮箱不存在，发送邮件失败");
        }

    }

    /**
     * 根据ID获取信息
     *
     * @param id id
     * @return 实体信息
     */
    @Override
    public UserRespDTO getInfoById(Long id) {
        User user = baseMapper.selectById(id);
        if (user == null) {
            throw new BusinessException(BaseErrorCode.NOT_FOUND_ERROR, "没有对应的信息");
        }
        return getUserVO(user);
    }

    @Override
    public UserRespDTO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserRespDTO userResp = new UserRespDTO();
        BeanUtils.copyProperties(user, userResp);
        return userResp;
    }

}
